package marketdepth;

import client.Client;
import client.ClientRegistry;
import exception.AddOrderException;
import order.OrderBuy;
import order.OrderSale;
import order.UnderdoneOrder;
import transaction.Transaction;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Logger;

public class MarketDepth implements Runnable {
    private static Logger log = Logger.getLogger(MarketDepth.class.getName());
    private final String[] sharesNames;
    private final Map<String, SingleMarketDepth> marketDepthMap;
    private final AtomicLong orderCounter;
    private final AtomicBoolean isChange;
    private final ClientRegistry clientRegistry;

    public MarketDepth(String[] sharesNames) {
        log.info("Start MarketDepth init");
        this.sharesNames = sharesNames;
        orderCounter = new AtomicLong(0);
        isChange = new AtomicBoolean(false);
        marketDepthMap =
                new HashMap<String, SingleMarketDepth>(sharesNames.length);
        for (String shareName : this.sharesNames) {
            marketDepthMap.put(shareName, new SingleMarketDepth());
        }
        clientRegistry = new ClientRegistry();
    }

    public boolean addClient(String clientName, Client client) {
        log.severe("Add client to ClientRegistry: " + clientName);
        return clientRegistry.addClient(clientName, client);
    }

    public boolean removeClient(String clientName) {
        log.severe("Remove client from ClientRegistry: " + clientName);
        return clientRegistry.removeClient(clientName);
    }

    public long addOrderBuy(String shareName,
                            UnderdoneOrder underdoneOrderBuy)
            throws AddOrderException {
        long orderId = orderCounter.incrementAndGet();
        log.severe("Add BUY Order: id=" + orderId + " shareName=" + shareName +
                " " + underdoneOrderBuy);
        addOrderBuy(orderId, shareName, underdoneOrderBuy);
        return orderId;
    }

    public long addOrderSale(String shareName,
                             UnderdoneOrder underdoneOrderSale)
            throws AddOrderException {
        long orderId = orderCounter.incrementAndGet();
        log.severe("Add SALE Order: id=" + orderId + " shareName=" + shareName +
                " " + underdoneOrderSale);
        addOrderSale(orderId, shareName, underdoneOrderSale);
        return orderId;
    }

    public void addOrderBuy(long orderId, String shareName,
                            UnderdoneOrder underdoneOrderBuy)
            throws AddOrderException {
        if (!Arrays.asList(sharesNames).contains(shareName)) {
            throw new AddOrderException("Stock is not found");
        }
        marketDepthMap.get(shareName).addOrderBuy(
                new OrderBuy(orderId, underdoneOrderBuy));
        isChange.set(true);
    }

    public void addOrderSale(long orderId, String shareName,
                             UnderdoneOrder underdoneOrderSale)
            throws AddOrderException {
        if (!Arrays.asList(sharesNames).contains(shareName)) {
            throw new AddOrderException("Stock is not found");
        }
        marketDepthMap.get(shareName).addOrderSale(
                new OrderSale(orderId, underdoneOrderSale));
        isChange.set(true);
    }

    @Override
    public void run() {
        log.info("Run MarketDepth");
        Transaction transaction;
        Client clientBuy;
        Client clientSale;
        while (true) {
            for (String shareName : this.sharesNames) {
                do {
                    transaction = marketDepthMap.get(shareName).fillOrders();
                    if (transaction != null) {
                        log.info("Filled orders");
                        try {
                            if (transaction.getRemainderOrder() != null) {
                                log.info("Remainder after filled");
                                switch (transaction.getRemainderOrder()
                                                            .getType()) {
                                    case BUY:
                                        addOrderBuy(
                                                transaction.getRemainderId(),
                                                shareName,
                                                transaction
                                                        .getRemainderOrder());
                                        break;
                                    case SALE:
                                        addOrderSale(
                                                transaction.getRemainderId(),
                                                shareName,
                                                transaction
                                                        .getRemainderOrder());
                                        break;
                                }
                            }
                        }
                        catch (AddOrderException e) {
                            log.severe("Error add remainder after filled");
                        }
                        clientBuy = clientRegistry.getClient(
                                transaction.getBuyClientName());
                        clientSale = clientRegistry.getClient(
                                transaction.getSaleClientName());
                        clientBuy.sendFilledData(transaction.getBuyId(),
                                transaction.getTransactionSize());
                        clientSale.sendFilledData(transaction.getSaleId(),
                                transaction.getTransactionSize());
                    }
                } while (transaction != null);
            }
            isChange.set(false);
            while (!isChange.get()) { //TODO: убрать цикл на что то внятное
            }
        }
    }
}
